<cfcomponent>
  <cfset _i = structNew() />
  <cfset _i.items = arrayNew(1) /><!--- only referred to directly in getItems() and setItems() --->
  <cfset _i.lastResult = "" />


  <cffunction name="init" output="false" returnType="org.coldquery.ColdQuery" >
    <cfargument name="items" type="any" required="true" />
    <cfset _i.componentName = getmetadata(this).name />
    <cfif isArray(arguments.items)>
      <cfset setItems( arguments.items ) />
    <cfelseif isQuery(arguments.items)>
      <cfset setItems(querytoarray(arguments.items)) />
    <cfelseif isObject(arguments.items) AND getMetaData(arguments.items).name EQ _i.componentName >
      <!--- it's one of us just grab the items --->
      <cfset setItems( arguments.items.getItems() ) />
    <cfelseif isStruct(arguments.items) >
      <!--- it's one of us just grab the items --->
      <cfset setItems( structToArray(arguments.items) ) />
    <cfelse>
      <cfthrow message="argument must be an array, query, or coldquery object">
    </cfif>
    <cfset this.$ = this.searchItems /><!--- shortcut --->
    <cfreturn this />
  </cffunction>

  <cffunction name="new" output="false" returnType="org.coldquery.ColdQuery"
              hint="create a new ColdQuery Object with the passed in Object">
    <cfargument name="items" type="any" required="true" />
    <cfreturn createObject("component","org.coldquery.ColdQuery").init(items).setlastResult(getLastResult()) />
  </cffunction>

  <cffunction name="get" output="false" returnType="any">
    <cfargument name="index" type="numeric" required="true" />
    <cfset var items = getItems() >
    <cfif getLength() GTE arguments.index AND arguments.index GT 0>
      <cfreturn items[arguments.index] />
    <cfelse>
      <cfthrow message="Index out of range - Index:#arguments.index# - Range:#arrayLen(getitems())#" type="#_i.componentName#.get.index_out_of_range_error" />
    </cfif>
  </cffunction>

  <cffunction name="getItems" output="false" returnType="array">
    <cfreturn _i.items />
  </cffunction>

  <cffunction name="setItems" output="false" returnType="org.coldquery.ColdQuery">
    <cfargument name="items" type="array" required="true" />
    <cfset _i.items = arguments.items />
    <cfreturn this />
  </cffunction>

  <cffunction name="searchItems" output="false" returnType="org.coldquery.ColdQuery" >
    <cfargument name="udfString" required="true" />
    <cfargument name="callerscope" required="false" default="#structNew()#" />
    <cfargument name="component" required="false" default="" />
    <cfset var i = 1 />
    <cfset var foundItems = ArrayNew(1) />
    <cfset var udf = "" />
    <cfset var result = "" />
    <cfset var callersc = arguments.callerscope />
    <cfset var container = "" />
    <cfset var items=getItems() />

    <cfif NOT isObject(arguments.component)>
      <cfset container = createObject("component","Container") />
    <cfelse>
      <cfset container = arguments.component />
    </cfif>
    <cfif isStruct(arguments.callerscope) >
      <cfset callersc= arguments.callerscope/>
    </cfif>

    <cftry>
      <cfset udf = createObject("component","UDFCreator").createUDF(arguments.udfString) />
      <cfif getmetadata(container).name EQ "org.coldquery.Container">
        <cfset container.setWorkerMethod(udf) />
      <cfelse>
        <cfset container.run = udf/>
      </cfif>
      <cfcatch>
        <cfthrow message="Could not evaluate udf String.  Please use a valid udf string"
                  detail="udfString:#arguments.udfString# - #cfcatch.message# - #cfcatch.detail#"
                  type="#_i.componentName#.searchItems.invalid_udfString" />
      </cfcatch>
    </cftry>


    <cfloop from="1" to="#arrayLen(items)#" index="i">
      <cftry>
        <cfset result = container.run(item=get(i),index=i,length=getLength(),scope=callersc) />
        <cfif result>
          <cfset arrayAppend(foundItems,get(i)) />
        </cfif>
        <cfcatch>
          <cfthrow message="each() - error evaluating udf: #cfcatch.message# - #cfcatch.detail#" />
        </cfcatch>
      </cftry>
    </cfloop>

    <cfreturn this.new(foundItems) />
  </cffunction>

  <cffunction name="each" output="false" returnType="org.coldquery.ColdQuery"
    hint="run a function on each item in the array.
      The function could modify the elements etc...
      the corresponding element in the returned array will be set to the result of the function">
    <cfargument name="udfString" required="true" hint="the udf string can have four ordered parameters (item,index,length,callerscope)" />
    <cfargument name="callerscope" required="false" default="#structNew()#" hint="pass in any variables that you want to be available in the function call" />
    <cfargument name="component" required="false" default="" hint="componentContext" />
    <cfset var i = 1 />
    <cfset var foundItems = ArrayNew(1) />
    <cfset var udf = "" />
    <cfset var res = "" />
    <cfset var container = ""/>
    <cfset var result = ArrayNew(1) />
    <cfset var ret = "" />
    <cfset var callersc = arguments.callerscope />

    <cfif NOT isObject(arguments.component)>
      <cfset container = createObject("component","Container") />
    <cfelse>
      <cfset container = arguments.component />
    </cfif>

    <cftry>
      <cfset udf = createObject("component","UDFCreator").createUDF(arguments.udfString) />
      <cfif getmetadata(container).name EQ "org.coldquery.Container">
        <cfset container.setWorkerMethod(udf) />
      <cfelse>
        <cfset container.run = udf/>
      </cfif>
      <cfcatch>
        <cfthrow message="Could not evaluate udf String.  Please use a valid udf string"
                  detail="udfString:#arguments.udfString# - #cfcatch.message# - #cfcatch.detail#"
                  type="#_i.componentName#.searchItems.invalid_udfString" />
      </cfcatch>
    </cftry>

    <cfloop from="1" to="#getLength()#" index="i">
      <cftry>
        <cfset result = container.run(item=get(i),index=i,length=getLength(),scope=callersc) />
        <cfset arrayAppend(foundItems,result) />
        <cfsavecontent variable="res">
          <cfdump var="#udfString#" />
        </cfsavecontent>
        <cfcatch type="json">
          <cfthrow message="each() - error evaluating udf:#res# - #cfcatch.message#" type="jsonencodederror" detail="#cfcatch.detail#" />
        </cfcatch>
        <cfcatch>
          <cfthrow message="error #getmetadata(this).name#.each() - error evaluating udf:#res# - #cfcatch.message#" type="jsonencodederror" detail="#serializeJson(cfcatch)#" />
        </cfcatch>
      </cftry>
    </cfloop>

    <cfreturn this.new(foundItems).setlastResult(container) />
  </cffunction>

  <cffunction name="writeEach" output="true" returnType="org.coldquery.ColdQuery"
    hint="run a function on each item in the array.
      The function could modify the elements etc...
      the return value of the function will be set to the
      corresponding element in the returned array and written to the page output">
    <cfargument name="udfString" required="true" />
    <cfargument name="callerscope" required="false" default="#structNew()#"
                hint="pass in any variables that you want to be available in the function call" />
    <cfset var i = 1 />
    <cfset var foundItems = ArrayNew(1) />
    <cfset var result = "" />
    <cfset var content= "" />
    <cfset var item = "" />

    <cfset result = this.each(argumentcollection=arguments) />

    <cfloop from="1" to="#result.getLength()#" index="i">
      <cfset item = result.get(i) />
      <cfif isSimpleValue(item) >
        <cfsavecontent variable="content"><cfoutput>#content##item#</cfoutput></cfsavecontent>
      <cfelse>
        <cfsavecontent variable="content"><cfoutput>#content#<cfdump var="#item#" /></cfoutput></cfsavecontent>
      </cfif>
    </cfloop>
    <cfoutput>#content#</cfoutput>
    <cfreturn result />
  </cffunction>

  <cffunction name="returnEachAsString" output="false" returnType="string"
    hint="run a function which returns a string on each item in the array
      all of the return values of the function calls will be
      concatenated and returned as a string">
    <cfargument name="udfString" required="true" />
    <cfargument name="callerscope" required="false" default="#structNew()#"
                hint="pass in any variables that you want to be available in the function call" />
    <cfset var i = 1 />
    <cfset var udf = "" />
    <cfset var result = ArrayNew(1) />
    <cfset var content= "" />
    <cfset var item = "" />

    <cfset result = this.each(argumentcollection=arguments) />
    <cfloop from="1" to="#result.getLength()#" index="i">
      <cfset item = result.get(i) />
      <cfif isSimpleValue(item) >
        <cfsavecontent variable="content"><cfoutput>#content##item#</cfoutput></cfsavecontent>
      <cfelse>
        <cfsavecontent variable="content"><cfoutput>#content#<cfdump var="#item#" /></cfoutput></cfsavecontent>
      </cfif>
    </cfloop>
    <cfreturn content />
  </cffunction>


  <cffunction name="sortByColumn" output="false" returnType="org.coldquery.ColdQuery"
              hint="sort using passed in criteria and return sorted">
    <cfargument name="expression" required="true" hint="must evaluate to the type specified in sorttype"/>
    <cfargument name="type" required="true" type="string" hint="String|StringNoCase|Numeric|date"/>
    <cfargument name="direction" required="true" hint="ASC|DESC"/>
    <cfset var sortedItems = arrayNew(1) />
    <cfset var sortCriteria = createObject("component","SortCriteria").init(argumentCollection=arguments) />
    <cfset getSortStrategy().addSortCriteria(sortCriteria) />
    <cfset sortedItems = getSortStrategy().sort(getItems()) />
    <cfreturn new(sortedItems) />
  </cffunction>

  <cffunction name="sortCustom" output="false" returnType="org.coldquery.ColdQuery"
        hint="
        <pre>
          f = createObject('component','org.coldquery.ColdQuery').init(myArray);
          UDF String Example:
          f.sortCustom('function f(obj1,obj2){
            if(obj1 GT obj2) {
              return 1
            }else if(obj1 EQ obj2){
              return 0;
            }
            return -1;
          }','asc');

          Predefined UDF Example:
          function f(obj1,obj2){
            if(obj1 GT obj2) {
              return 1
            }else if(obj1 EQ obj2){
              return 0;
            }
            return -1;
          }

          obj = obj.sortCustom(f,'asc');</pre>" >

    <cfargument name="udf" required="true" hint="A udf or a string that can be evaluated to create a udf." />
    <cfargument name="direction" required="true" hint="ASC|DESC" />
    <cfset var sortedItems = arrayNew(1) />
    <cfset var sortCriteria = "" />

    <cfif not isCustomFunction(arguments.udf)>
      <cftry>
        <cfset arguments.udf = createObject("component","UDFCreator").createUDF(arguments.udf) />
        <cfcatch>
          <cfthrow message="Could not evaluate udf String.  Please use a valid udf string (3)"
            detail="udfString:#arguments.udf# - #cfcatch.message# - #cfcatch.detail#"
            type="#_i.componentName#.sortCustom.invalid_udfString" />
        </cfcatch>
      </cftry>
    </cfif>

    <!--- now we have a udf --->
    <cfset arguments.type = arguments.udf />
    <cfset arguments.expression= "" />

    <cfset sortCriteria = createObject("component","SortCriteria").init(argumentCollection=arguments) />
    <cfset getSortStrategy().addSortCriteria(sortCriteria) />
    <cfset sortedItems = getSortStrategy().sort(getItems()) />
    <cfreturn this.new(sortedItems)/>
  </cffunction>

  <cffunction name="reverseItems" output="false" returnType="org.coldquery.ColdQuery">
    <cfset var retArr = arrayNew(1) />
    <cfset var tmpArr = getItems() />
    <cfset var i = getItems() />
    <cfloop from="1" to="#arrayLen(tmpArr)#" index="i">
      <cfset arrayAppend(retArr,tmpArr[arrayLen(tmpArr)+1-i]) />
    </cfloop>
    <cfreturn this.new(retArr) />
  </cffunction>


  <cffunction name="even" output="false" returnType="org.coldquery.ColdQuery">
    <cfset var retArr = arrayNew(1) />
    <cfset var tmpArr = getItems() />
    <cfset var i = getItems() />
    <cfif arraylen(tmpArr) GTE 2>
      <cfloop from="2" to="#arrayLen(tmpArr)#" index="i" step="2">
        <cfset arrayAppend(retArr,tmpArr[i]) />
      </cfloop>
    </cfif>
    <cfreturn this.new(retArr) />
  </cffunction>

  <cffunction name="odd" output="false" returnType="org.coldquery.ColdQuery">
    <cfset var retArr = arrayNew(1) />
    <cfset var tmpArr = getItems() />
    <cfset var i = getItems() />
    <cfif arraylen(tmpArr) GTE 1>
      <cfloop from="1" to="#arrayLen(tmpArr)#" index="i" step="2">
        <cfset arrayAppend(retArr,tmpArr[i]) />
      </cfloop>
    </cfif>
    <cfreturn this.new(retArr)/>
  </cffunction>


  <cffunction name="setSortStrategy" output="false" access="public">
    <cfargument name="sortStrategy" required="true"/>
    <cfset _i.sortStrategy = arguments.sortStrategy />
  </cffunction>

  <cffunction name="getSortStrategy" output="false" access="private">
    <cfif not structKeyExists(_i,"sortStrategy") >
      <cfset _i.sortStrategy = createObject("component","QuickSortStrategy").init() />
    </cfif>
    <cfreturn _i.sortStrategy />
  </cffunction>

  <cffunction name="getLength" output="false" returntype="numeric" access="public">
    <cfreturn arrayLen(getItems()) />
  </cffunction>

  <cffunction name="concat" output="false" returntype="org.coldquery.ColdQuery" access="public">
    <cfargument name="items" type="any" required="true">
    <!--- get items as an array --->
    <cfset var itemsArray = this.new(arguments.items).getItems() />
    <cfset var i = "" />
    <cfset var newItems = getItems() />
    <cfloop from="1" to="#arrayLen(itemsArray)#" index="i">
      <cfset arrayAppend(newItems,itemsArray[i]) />
    </cfloop>
    <cfreturn this.new(newItems) />
  </cffunction>

  <cffunction name="range" output="false" returntype="org.coldquery.ColdQuery" access="public"
              hint="get a range of items and return the new coldquery object">
    <cfargument name="start" type="numeric" required="true">
    <cfargument name="end" type="numeric" required="true">
    <!--- get items as an array --->
    <cfset var oldItems = getItems() />
    <cfset var newItems = arrayNew(1) />
    <cfset var i = "" />
    <cfloop from="#arguments.start#" to="#arguments.end#" index="i">
      <cfif arrayLen(oldItems) GTE i >
        <cfset arrayAppend(newItems, oldItems[i]) />
      <cfelse>
        <cfbreak>
      </cfif>
    </cfloop>
    <cfreturn this.new(newItems)/>
  </cffunction>


  <cffunction name="append" output="false" returntype="org.coldquery.ColdQuery" access="public"
              hint="Append the value to the current array">
    <cfargument name="value" type="any" required="true">
    <!--- get items as an array --->
    <cfset var newItems = getItems() />
    <cfset arrayAppend( newItems, arguments.value ) />
    <cfreturn this.new(newItems) />
  </cffunction>

  <cffunction name="queryToArray" access="public" returntype="array" output="false"
  	hint="This turns a query into an array of structures.  Created By Ben Nadel.">
  	<!--- Define arguments. --->
  	<cfargument name="Data" type="query" required="yes" />
  	<cfscript>
  		// Define the local scope.
  		var LOCAL = StructNew();
  		// Get the column names as an array.
  		LOCAL.Columns = ListToArray( ARGUMENTS.Data.ColumnList );
  		// Create an array that will hold the query equivalent.
  		LOCAL.QueryArray = ArrayNew( 1 );
  		// Loop over the query.
  		for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount ; LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
  			// Create a row structure.
  			LOCAL.Row = StructNew();
  			// Loop over the columns in this row.
  			for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen( LOCAL.Columns ) ; LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
  				// Get a reference to the query column.
  				LOCAL.ColumnName = LOCAL.Columns[ LOCAL.ColumnIndex ];
  				// Store the query cell value into the struct by key.
  				LOCAL.Row[ LOCAL.ColumnName ] = ARGUMENTS.Data[ LOCAL.ColumnName ][ LOCAL.RowIndex ];
  			}
  			// Add the structure to the query array.
  			ArrayAppend( LOCAL.QueryArray, LOCAL.Row );
  		}
  		// Return the array equivalent.
  		return( LOCAL.QueryArray );
  	</cfscript>
  </cffunction>

  <cffunction name="toQuery" access="public" returntype="any" output="false"
  	hint="This turns an array into a query object.  Created By Lee Howard.">
  	<!--- Define arguments. --->
  	<cfset var local = structNew() />
  	<cfset local.types = "" />
  	<cfset local.i = 0 />
  	<cfset local.colName = "" />
  	<cfif getLength() GTE 1 AND isStruct(get(1)) >
  	  <cfset local.asStructOfArr= structNew() />
  	  <cftry>
    	  <cfset this.each(udfString=testMethod,callerscope=local,component=this) />
    	  <cfcatch type="jsonencodederror">
      	  <cfthrow type="jsonencodederror" message="#getmetadata(this).name#.toQuery() - #cfcatch.message#" detail="#cfcatch.detail#">
        </cfcatch>
    	  <cfcatch>
      	  <cfthrow type="jsonencodederror" message="#getmetadata(this).name#.toQuery() - #cfcatch.message#" detail="#serializeJson(cfcatch)#">
        </cfcatch>
      </cftry>
      <cfset local.retQuery = queryNew("") />
  	  <cfloop from="1" to="#listLen(structKeyList(local.asStructOfArr))#" index="local.i">
  	    <cfif arrayLen(local.asStructOfArr[listGetAt(structKeyList(local.asStructOfArr),local.i)]) EQ getLength()>
          <cfif isNumeric(listGetAt(structKeyList(local.asStructOfArr),local.i))>
            <cfset local.colName = 'a_' & right('0000' & listGetAt(structKeyList(local.asStructOfArr),local.i),4) />
          <cfelse>
            <cfset local.colName = listGetAt(structKeyList(local.asStructOfArr),local.i) />
          </cfif>
          <cfset queryAddColumn(local.retQuery,local.colname,local.asStructOfArr[listGetAt(structKeyList(local.asStructOfArr),local.i)]) />
  	    </cfif>
  	  </cfloop>
  	<cfelse>
  	  <cfreturn queryNew("x") />
  	</cfif>
	  <cfreturn local.retQuery />
  </cffunction>


  <cffunction name="testMethod" access="private" output="false">
    <cfargument name="item">
    <cfargument name="index">
    <cfargument name="length">
    <cfargument name="scope">
    <cfscript>
        var i = 1;
        var x = 1;
        var col = '';
        for(i=1;i LTE listlen(structKeyList(arguments.item));i=i+1){
          col = listGetAt(structKeyList(arguments.item),i);
          if(not structKeyExists(arguments.scope.asStructOfArr,col)){
            arguments.scope.asStructOfArr[col] = ArrayNew(1);
            for(x EQ 1;x LT arguments.index;x=x+1){
              ArrayAppend(arguments.scope.asStructOfArr[col]," ");
            }
          }
          arrayAppend(arguments.scope.asStructOfArr[col],arguments.item[col]);
        }
        return 1;
    </cfscript>
  </cffunction>

  <cffunction name="Inspect" output="false">
    <cfset var res = "" />
    <cftry>
      <cfset res = SerializeJSON( this.getItems() )  />
      <cfcatch>
        <cfreturn "ColdQuery: inspect not supported for item of this type" />
      </cfcatch>
    </cftry>
    <cfreturn res />
  </cffunction>

  <cffunction name="getDumpQuery" output="false">
    <cfset var res = "" />
    <cfsavecontent variable="res">
      <cfdump var="#this.toQuery()#" />
    </cfsavecontent>
    <cfreturn res />
  </cffunction>


  <cffunction name="getDump" output="false">
    <cfset var res = "" />
    <cfsavecontent variable="res">
      <cfdump var="#this.getItems()#" />
    </cfsavecontent>
    <cfreturn res />
  </cffunction>


  <cffunction name="structToArray" access="public" returntype="array" output="false"
  	hint="This turns a struct into an array where each value is a struct with name and value keys.">
  	<!--- Define arguments. --->
  	<cfargument name="Data" type="struct" required="yes" />
  	<cfset var i = "" />
  	<cfset var ret = ArrayNew(1) />
  	<cfset var tmp = "" />
  	<cfloop list="#structKeyList(arguments.data)#" index="i" >
  	  <cfset tmp = structNew()  />
      <cfset tmp.name=i />
      <cfset tmp.value=arguments.data[i] />
      <cfset arrayAppend(ret,tmp) />
  	</cfloop>
  	<cfreturn ret />
  </cffunction>

  <cffunction name="setLastResult" output="false" access="public">
    <cfargument name="value" required="true" />
    <cfset _i.lastResult = arguments.value />
    <cfreturn this />
  </cffunction>

  <cffunction name="getLastResult" output="false">
    <cfreturn _i.lastResult />
  </cffunction>

</cfcomponent>
